package dao

import (
	"context"
	"fmt"
	"gorm.io/gorm"
	"time"
	"todo-list/repository/db/model"
)

type TaskDao struct {
	DB          *gorm.DB
	Transaction bool
}

func (dao *TaskDao) FindById(taskId uint) (task model.Task, err error) {
	err = dao.DB.Model(&task).Take(&task, taskId).Error
	return
}

func (dao *TaskDao) FindByIds(taskIds []uint) (tasks []model.Task, err error) {
	err = dao.DB.Model(&model.Task{}).Where("id in ?", taskIds).Find(&tasks).Error
	return
}

func (dao *TaskDao) FindTask(param model.ModelMap, optional *OptionalParam) (tasks []model.Task, rows int64, err error) {
	tx := dao.DB.Order("id desc").Scopes(dao.queryByOptionalParam(optional))
	status, exists := param["status"]
	if exists {
		tx.Scopes(dao.queryByStatus(status.(model.TaskStatus)))
	}
	text, exists := param["text"]
	if exists {
		tx.Scopes(dao.queryByText(text.(string)))
	}
	public, exists := param["public"]
	if exists {
		tx.Scopes(dao.queryByPublic(public.(bool)))
	}
	createAt, exists := param["createAt"]
	if exists {
		tx.Scopes(dao.queryByCreateTime(createAt.(int64)))
	}
	err = tx.Find(&tasks).Limit(-1).Offset(-1).Count(&rows).Error
	return
}

func (dao *TaskDao) queryByOptionalParam(optional *OptionalParam) func(*gorm.DB) *gorm.DB {
	return func(db *gorm.DB) *gorm.DB {
		tx := db.Model(&model.Task{})
		if optional == nil {
			return tx
		}
		if optional.IgnoreContent {
			tx = tx.Omit("content")
		}
		if page, limit := optional.Page, optional.Limit; page != nil && limit != nil {
			tx = tx.Limit(*limit).Offset((*page - 1) * *limit)
		}
		if userId := optional.Id; userId != nil {
			tx = tx.Where("user_id = ?", userId)
		}
		return tx
	}
}

func (dao *TaskDao) queryByPublic(public bool) func(*gorm.DB) *gorm.DB {
	return func(db *gorm.DB) *gorm.DB {
		return db.
			Where("public = ?", public)
	}
}

func (dao *TaskDao) queryByCreateTime(createAt int64) func(*gorm.DB) *gorm.DB {
	return func(db *gorm.DB) *gorm.DB {
		return db.
			Where("created_at like ?", fmt.Sprintf("%s %%", time.UnixMilli(createAt).Format(time.DateOnly)))
	}
}

func (dao *TaskDao) queryByText(text string) func(*gorm.DB) *gorm.DB {
	return func(db *gorm.DB) *gorm.DB {
		return db.
			Where("title like ?", fmt.Sprintf("%%%s%%", text)).
			Or("content like ?", fmt.Sprintf("%%%s%%", text))
	}
}

func (dao *TaskDao) queryByStatus(status model.TaskStatus) func(*gorm.DB) *gorm.DB {
	return func(db *gorm.DB) *gorm.DB {
		return db.Where("status = ?", status)
	}
}

func (dao *TaskDao) CreateTask(task *model.Task) (err error) {
	err = dao.DB.Model(task).Create(task).Error
	return
}

func (dao *TaskDao) UpdateTask(task *model.Task, userId uint) (err error) {
	err = dao.DB.Model(task).Select("*").Where("user_id = ?", userId).Save(task).Error
	return
}

func (dao *TaskDao) UseTransaction() {
	dao.DB = dao.DB.Begin()
	dao.Transaction = true
}

func (dao *TaskDao) Rollback() {
	if !dao.Transaction {
		return
	}
	dao.DB.Rollback()
}

func (dao *TaskDao) Commit() {
	if !dao.Transaction {
		return
	}
	dao.DB.Commit()
}

func NewTaskDao(ctx context.Context) *TaskDao {
	if ctx == nil {
		ctx = context.Background()
	}
	return &TaskDao{DB: NewDBClient(ctx)}
}
